package cn.ithers.blog.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.ithers.blog.dao.UserDao;
import cn.ithers.blog.manager.CountDownLatchManager;
import cn.ithers.blog.pojo.User;
import cn.ithers.blog.response.ResponseResult;
import cn.ithers.blog.service.IQrCodeService;
import cn.ithers.blog.service.IUserService;
import cn.ithers.blog.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * @Author: zhanghuan
 * @date: 2020/10/20 10:35
 * @description:
 */

@Slf4j
@Service
public class QrCodeServiceImpl implements IQrCodeService {


    @Autowired
    private IdWorker idWorker;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private CountDownLatchManager countDownLatchManager;

    @Autowired
    private IUserService userService;

    @Autowired
    private UserDao userDao;


    /**
     * 获取二维码
     * 二维码的图片路径
     * 二维码的code
     *
     * @return
     */
    @Override
    public ResponseResult getPcQrCodeInfo() {
        //尝试取出上一次的loginId
        String lastLoginId = CookieUtils.getCookie(userService.getRequest(), Constants.QR.LAST_REQUEST_LOGIN_ID);
        if (!StrUtil.isEmpty(lastLoginId)) {
            //先把redis里的删除
            redisUtils.del(Constants.QR.KEY_PC_LOGIN_ID + lastLoginId);
            //检查上次的请求时间，如果太频繁，则直接骂人
            Object lastGetTime = redisUtils.get(Constants.QR.LAST_REQUEST_LOGIN_ID + lastLoginId);
            if (lastGetTime != null) {
                return ResponseResult.FAILED("服务器繁忙，请稍后重试.");
            }
        }
        // 1、生成一个唯一的ID
        long code = idWorker.nextId();
        // 2、保存到redis里，值为false，时间为5分钟（二维码的有效期）
        //todo:修改回来5分钟
        redisUtils.set(Constants.QR.KEY_PC_LOGIN_ID + code,
                Constants.QR.KEY_PC_LOGIN_STATE_FALSE,
                Constants.TimeValueInSecond.MIN_5 * 10000);
        Map<String, Object> result = new HashMap<>();
        String originalDomain = CustomUtils.getDomain(userService.getRequest());
        result.put("code", code);
        result.put("url", originalDomain + "/portal/image/qr_code/" + code);
        CookieUtils.setUpCookie(userService.getResponse(), Constants.QR.LAST_REQUEST_LOGIN_ID, String.valueOf(code));
        //todo:需要修改为10秒
        redisUtils.set(Constants.QR.LAST_REQUEST_LOGIN_ID + String.valueOf(code),
                "true", Constants.TimeValueInSecond.SECOND_10 * 100000000);
        // 返回结果
        return ResponseResult.SUCCESS("获取成功.").setData(result);
    }

    /**
     * 检查二维码的登录状态
     * 结果有：
     * 1、登录成功（loginId对应的值为有ID内容）
     * 2、等待扫描（loginId对应的值为false）
     * 3、二维码已经过期了 loginId对应的值为null
     * <p>
     * 是被PC端轮询调用的
     *
     * @param loginId
     * @return
     */
    @Override
    public ResponseResult checkQrCodeLoginState(String loginId) {
        log.info("loginId-------->" + loginId);
        //从redis里取值出来
        ResponseResult result = checkLoginIdState(loginId);
        log.info("result------->" + result);
        if (result != null) return result;
        //先等待一段时间，再去检查
        //如果超出了这个时间，我就们就返回等待扫码
        Callable<ResponseResult> callable = new Callable<ResponseResult>() {
            @Override
            public ResponseResult call() throws Exception {
                try {
                    log.info("start waiting for scan...");
                    //先阻塞
                    countDownLatchManager.getLatch(loginId).await(Constants.QR.QR_CODE_STATE_CHECK_WAITING_TIME,
                            TimeUnit.SECONDS);
                    //收到状态更新的通知，我们就检查loginId对应的状态
                    log.info("start check login state...");
                    ResponseResult checkResult = checkLoginIdState(loginId);
                    if (checkResult != null) return checkResult;
                    //超时则返回等待扫描
                    //完事后，删除对应的latch
                    return ResponseResult.WAiTING_FOR_SCAN();
                } finally {
                    log.info("delete latch...");
                    countDownLatchManager.deleteLatch(loginId);
                }
            }
        };
        try {
            return callable.call();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.WAiTING_FOR_SCAN();
    }

    /**
     * 检查二维码对应的 唯一 flag 的 状态
     *
     * @param loginId 二维码对应的 唯一 flag
     * @return 自定义的返回对象
     */
    public ResponseResult checkLoginIdState(String loginId) {
        String loginState = (String) redisUtils.get(Constants.QR.KEY_PC_LOGIN_ID + loginId);
        log.info("loginState------->" + loginState);

        if (StrUtil.isEmpty(loginState)) {
            return ResponseResult.FAILED("二维码过期了");
        }
        //如果loginState为true 表示扫码成功
        if (!loginState.equals(String.valueOf(false))) {

            userService.createToken(userService.getResponse(), userDao.findOneById(loginId), Constants.FROM_PC);
            // 扫码登录成功
            return ResponseResult.SUCCESS("扫码登录成功");
        }
        //loginState为false时返回空;

        return null;
    }

    /**
     * 更新二维码的登录状态
     *
     * @param loginId
     * @return
     */
    @Override
    public ResponseResult updateQrCodeLoginState(String loginId) {
        //1、检查用户是否登录
        User user = userService.checkUser();
        if (user == null) {
            return ResponseResult.ACCOUNT_NOT_LOGIN();
        }
        //2、改变loginId对应的值=true
        redisUtils.set(Constants.QR.KEY_PC_LOGIN_ID + loginId, user.getId());
        //2.1、通知正在等待的扫描任务
        countDownLatchManager.onPhoneDoLogin(loginId);
        //3、返回结果
        return ResponseResult.SUCCESS("登录成功.");
    }

}
